﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.TextFormatting;
using System.Windows.Documents;
using System.Globalization;
using System.Windows;

namespace SDF.TextFormatting
{
    public class SmartTextSource: TextSource
    {
        #region Constructor

        public SmartTextSource(SmartInlineCollection source)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            Source = source;

            CultureInfo = CultureInfo.CurrentUICulture;
            Length = 0;
            Fragments = new List<SmartTextFragment>();
            ExtractFragments(source);
        }

        private void ExtractFragments(SmartInlineCollection inlines)
        {
            foreach (var inline in inlines)
                ExtractFragments(inline);

            var sb = new StringBuilder();
            foreach (var fragment in Fragments)
                if (fragment.Source is TextRun)
                    sb.Append(((TextRun)fragment.Source).Text);

            FullText = sb.ToString();
        }

        private void ExtractFragments(SmartInline inline)
        {

            // If it is a span, just grab the inside fragments
            var span = inline as Span;
            if (span != null)
            {
                ExtractFragments(span.Inlines);
                return;
            }

            var run = inline as TextRun;
            if (run != null)
            {
                AddFragment(new SmartTextFragment(run));
                return;
            }

            var lineBreak = inline as LineBreak;
            if (lineBreak != null)
            {
                AddFragment(SmartTextFragment.LineBreak);
                return;
            }

            var container = inline as UIElementContainer;
            if (container != null)
            {
                AddFragment(new SmartTextFragment(container));
                return;
            }

            var figure = inline as Figure;
            if (figure != null)
            {
                // Ignore figures. They would have been handled and drawn by the paragraph before the actual text
                return;
            }
            
            throw new NotSupportedException(String.Format("Type '{0}' is not supported by SmartTextSource.", inline.GetType()));
        }

        private void AddFragment(SmartTextFragment fragment)
        {
            Fragments.Add(fragment);
            Length += fragment.Length;
            return;
        }

        #endregion

        #region Properties

        public int Length { get; private set; }

        protected SmartInlineCollection Source { get; private set; }

        protected List<SmartTextFragment> Fragments { get; private set; }

        public CultureInfo CultureInfo { get; set; }

        public string FullText { get; private set; }

        #endregion

        #region Methods

        public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
        {
            CharacterBufferRange cbr = new CharacterBufferRange(FullText, 0, textSourceCharacterIndexLimit);
            return new TextSpan<CultureSpecificCharacterBufferRange>(
             textSourceCharacterIndexLimit,
             new CultureSpecificCharacterBufferRange(CultureInfo, cbr)
             );
        }

        public override System.Windows.Media.TextFormatting.TextRun GetTextRun(int textSourceCharacterIndex)
        {
            // Make sure text source index is in bounds.
            if (textSourceCharacterIndex < 0)
                throw new ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.");

            if (textSourceCharacterIndex >= Length)
            {
                return new TextEndOfParagraph(1);
            }

            int offset = 0;
            var fragment = GetFragmentAtIndex(textSourceCharacterIndex, out offset);

            return fragment.GetTextRun(offset);
        }

        private SmartTextFragment GetFragmentAtIndex(int textSourceCharacterIndex, out int offset)
        {
            int runningLength = 0;

            foreach (var fragment in Fragments)
            {
                if (textSourceCharacterIndex < fragment.Length + runningLength)
                {
                    offset = textSourceCharacterIndex - runningLength;
                    return fragment;
                }

                runningLength += fragment.Length;
            }

            offset = 0;
            return null;
        }

        public override int GetTextEffectCharacterIndexFromTextSourceCharacterIndex(int textSourceCharacterIndex)
        {
            throw new NotSupportedException("This method is not supported");
        }

        #endregion

    }
}
